import * as THREE from 'three'
import * as d3 from 'd3'
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { gsap } from 'gsap'

export const timer = {}
export const onMouseMove1 = {}
export const onMouseMove2 = {}
export const onMouseMove3 = {}
export const onMouseMove4 = {}
export const onClick = {}
export const onMouseDown = {}
export const _xishu = {}
export const _map = {}
export const _offsetXY = {}
export const _camera = {}

let timestamp

let currentBarHighlight = null
let currentScatterHighlight = null

// 获取区块中心点
export const setCenter = (map) => {
  map.rotation.x = -Math.PI / 2
  const box = new THREE.Box3().setFromObject(map)
  const center = box.getCenter(new THREE.Vector3())

  const offset = [0, 0]
  map.position.x = map.position.x - center.x - offset[0]
  map.position.z = map.position.z - center.z - offset[1]
}

// 创建边界线
export const createLine = (data, mapConfig, offsetXY, xishu) => {
  const points = [] as any
  data.forEach((item) => {
    const [x, y] = offsetXY(item)
    points.push(new THREE.Vector3(x, -y, 0))
  })
  const lineGeometry = new THREE.BufferGeometry().setFromPoints(points)
  const uplineMaterial = new THREE.LineBasicMaterial(mapConfig.lineMaterial)

  const path = new THREE.CatmullRomCurve3(points)
  const { radius, ...bottomLineMaterial } = mapConfig.bottomLine
  const tubeGeometry = new THREE.TubeGeometry(path, 500, radius * xishu, 8, false)
  const material = new THREE.MeshBasicMaterial(bottomLineMaterial)
  const downTube = new THREE.Mesh(tubeGeometry, material)
  downTube.name = 'downTube'
  const upLine = new THREE.Line(lineGeometry, uplineMaterial)
  upLine.position.z = mapConfig.depth * xishu + 0.01
  upLine.name = 'upLine'
  return [upLine, downTube]
}

// 创建地图板块
export const createMesh = (
  data,
  mapConfig,
  offsetXY,
  blockData,
  blockConfig,
  name,
  center,
  xishu
) => {
  const shape = new THREE.Shape()
  data.forEach((item, idx) => {
    const [x, y] = offsetXY(item)

    if (idx === 0) shape.moveTo(x, -y)
    else shape.lineTo(x, -y)
  })

  const geometry = new THREE.ExtrudeGeometry(shape, {
    depth: mapConfig.depth * xishu,
    bevelEnabled: false
  })
  const materialParams = { ...mapConfig.material }
  let blockTip = null
  if (blockData) {
    const blockItem = blockData.find((item) => item.name === name)
    if (blockConfig.colors && blockItem) {
      // 线性插值
      // 颜色转换
      const hexToRgb = (hex) => {
        // 去除可能的 # 符号
        hex = hex.replace(/^#/, '')
        // 将颜色值分解成三个部分：红色、绿色和蓝色
        const bigint = parseInt(hex, 16)
        const r = (bigint >> 16) & 255
        const g = (bigint >> 8) & 255
        const b = bigint & 255
        return { r, g, b }
      }
      // 线性插值函数
      const lerp = (start, end, t) => {
        return start * (1 - t) + end * t
      }
      const color1 = hexToRgb(blockConfig.colors[0])
      const color2 = hexToRgb(blockConfig.colors[1])
      const maxValue = Math.max(...blockData.map((item) => item.value))
      const minValue = Math.min(...blockData.map((item) => item.value))
      // 将数值映射到 [0, 1] 范围
      const tValue = (blockItem.value - minValue) / (maxValue - minValue)
      // 将最大到最小分为5等分，计算tValue所对应的等分t值，根据 t 值计算颜色插值的位置
      const index = Math.floor(tValue * 5)
      const tArr = [0, 0.25, 0.5, 0.75, 1, 1]
      const t = tArr[index]
      // 对每个颜色通道进行线性插值
      const interpolatedColor = {
        r: Math.round(lerp(color1.r, color2.r, t)),
        g: Math.round(lerp(color1.g, color2.g, t)),
        b: Math.round(lerp(color1.b, color2.b, t))
      }
      materialParams.color = `rgb(${interpolatedColor.r},${interpolatedColor.g},${interpolatedColor.b})`
    }

    // label 文字
    const div = document.createElement('div')
    Object.entries(blockConfig.labelStyle).forEach(([key, value]) => {
      div.style[key] = value
    })
    if (blockConfig.labelFormatter && blockItem) {
      blockItem.innerHTML = blockConfig.labelFormatter(blockItem)
    } else {
      div.innerHTML = [
        `<div style="font-weight: bold;margin-bottom: 8px;">${blockItem?.name}</div>`,
        ...(blockItem?.info || []).map(
          (item) => `
          <div style="display:flex">
            <span style="color:#ccc">${item.title}：</span>
            <span style="flex: 1">${item.content}</span>
          </div>`
        )
      ].join('')
    }
    const tip = new CSS2DObject(div)
    tip.name = 'block-tip-' + name
    const [x, y] = offsetXY(center)
    tip.position.set(x, -y, mapConfig.depth * xishu + 0.8)
    tip.visible = false
    tip.renderOrder = 2
    blockTip = tip
  }

  const material = new THREE.MeshStandardMaterial(materialParams)
  const sideMaterial = new THREE.MeshStandardMaterial(mapConfig.sideMaterial)
  const mesh = new THREE.Mesh(geometry, [material, sideMaterial])
  mesh.position.set(0, 0, 0)
  mesh.name = 'map'
  if (blockTip) mesh.add(blockTip)
  return mesh
}

// 创建名称
export const createLabel = (name, point, mapConfig, offsetXY, blockData, blockConfig, xishu) => {
  const div = document.createElement('div')
  const nameDiv = document.createElement('div')
  Object.entries(mapConfig.labelStyle).forEach(([key, value]) => {
    nameDiv.style[key] = value
  })
  nameDiv.textContent = name
  const tooltip = document.createElement('div')
  blockConfig.labelStyle &&
    Object.entries(blockConfig.labelStyle).forEach(([key, value]) => {
      tooltip.style[key] = value
    })
  div.appendChild(nameDiv)
  if (blockData) {
    const blockItem = blockData.find((item) => item.name === name)
    if (blockItem) {
      if (blockConfig.labelFormatter) {
        tooltip.innerHTML = blockConfig.labelFormatter(blockItem)
      } else {
        tooltip.innerHTML = [
          `<div style="font-weight: bold;margin-bottom: 8px;">${blockItem.name}</div>`,
          ...(blockItem.info || []).map(
            (item) => `
            <div style="display:flex">
              <span style="color:#ccc">${item.title}：</span>
              <span style="flex: 1">${item.content}</span>
            </div>`
          )
        ].join('')
      }
      div.appendChild(tooltip)
      // const arrow = document.createElement('img')
      // arrow.src = [
      //   '',
      //   'A6yNxSAAAAAXNSR0IArs4c6QAAAjZJREFUSEu1lUFo02AUx///hBLQYaYTBDc',
      //   'v4g6iw5MHh2UThE3w4mVSLyKKB6FIJ7qbyG5T1iEFD6KIF8s8C24gWKnMgyep4',
      //   'mHiZZsgOG1lCqEkT9I2XZsm+dKpOeTLl3zv/9733vt+4Zlbq7MiMgn3Yu3e+tCYi',
      //   '/ehPvqmoGfo/xBs7q0nkCUgPH3zSx6Cs+1eImYKf7F0iPln03tTNalT6WVDzJ4Fi',
      //   'IwGZ8Av6e20mbIQnx2p8uQLrGyMP88NWk2Fk1PfTWjVIiBDsXaw5UUswUkkX8zsq',
      //   'nQUe/TG+gBgL4HYF60fNwNeDzTXrwD6cOF232pYt+HE1Pqhqu28BtAbGoQ/8yG91',
      //   '27PckLn8ZczfR9a3wcW8dj1HyNi24sAjS1nus1QLOr62Js7O1/59UK76GimPOGI5',
      //   'AnRNo26b38BHY1MvZ3rfRq0mcg2PnK1kqFI9m+yIOTku7vmXJiG6hxhKP1zVpwwU',
      //   'EWDhxqzpdyOa1EbUAbggurglY28dAMql6XE/Md7PSmAkS0aIwDgQHrZQLV/AeI0Q',
      //   'BV2iBq+qBWQWBv/lBu0VOWLFYArsv+ymAKrKOJEgorUSoSR/HyfNdCortgBuEL9l',
      //   '34PULQloA6qANCuCJ3htQfbmqD5pwG4YnsuWodhs0jIJqjcXxpYhibJrw+N9yqnS',
      //   'hCpBHZfqI7AwSIEdVARFjSMfXuU6ACNSqurErSKmedlgraTr5VC11KVxwwEzX8Lw',
      //   'BXefk4y7vjrCUNBowrgDzmRq1WMFaK2AAAAAElFTkSuQmCC'
      // ].join('')
      // arrow.style.width = '16px'
      // arrow.style.height = '9px'
      // arrow.style.position = 'absolute'
      // arrow.style.left = '50%'
      // arrow.style.bottom = '-16px'
      // arrow.style.transform = 'translate(-50%, 0)'
      // tooltip.appendChild(arrow)
      if (blockItem.icon) {
        const icon = document.createElement('img')
        icon.src = blockItem.icon
        icon.style.width = '46px'
        icon.style.height = '46px'
        icon.style.position = 'absolute'
        icon.style.left = '50%'
        icon.style.bottom = '-60px'
        icon.style.transform = 'translate(-50%, 0)'
        tooltip.appendChild(icon)
      }
    }
  }
  const label = new CSS2DObject(div)
  // label.scale.set(0.01, 0.01, 0.01);
  const [x, y] = offsetXY(point) //地图坐标系转换成空间坐标
  label.position.set(x, -y, mapConfig.depth * xishu)
  label.name = 'label-' + name
  label.renderOrder = 2
  return label
}

// 创建标记
const createScatter = (scatterDataItem, depth, offsetXY, scatterConfig, intro, xishu) => {
  const data = scatterDataItem
  const url = new URL(data.icon || scatterConfig.icon, import.meta.url).href
  const map = new THREE.TextureLoader().load(url)
  const urlSelected = new URL(
    data.iconSelected || data.icon || scatterConfig.iconSelected || scatterConfig.icon,
    import.meta.url
  ).href
  const mapSelected = new THREE.TextureLoader().load(urlSelected)
  const material = new THREE.SpriteMaterial({
    map,
    transparent: true
  })
  const pin = new THREE.Sprite(material)
  const [x, y] = offsetXY(data.position)
  pin.iconData = data
  pin.name = 'pin'
  pin.renderOrder = 5

  const materialSelected = new THREE.SpriteMaterial({
    map: mapSelected,
    transparent: true
  })
  const pinSelected = new THREE.Sprite(materialSelected)
  pinSelected.iconData = data
  pinSelected.name = 'pin-selected'
  pinSelected.visible = false
  pinSelected.renderOrder = 5

  // label 文字
  const div = document.createElement('div')
  Object.entries(scatterConfig.labelStyle).forEach(([key, value]) => {
    div.style[key] = value
  })
  if (scatterConfig.labelFormatter) {
    div.innerHTML = scatterConfig.labelFormatter(data)
  } else {
    div.innerHTML = [
      `<div style="font-weight: bold;margin-bottom: 8px;">${data.name}</div>`,
      ...(data.info || []).map(
        (item) => `
        <div style="display:flex">
          <span style="color:#ccc;width:80px">${item.title}：</span>
          <span style="flex: 1">${item.content}</span>
        </div>`
      )
    ].join('')
  }
  const tip = new CSS2DObject(div)

  const tipPosition = scatterConfig.labelPosition || { x: 0, y: 0 }
  tip.position.set(tipPosition.x, tipPosition.y, 0)
  tip.name = 'icon-tip-' + data.name
  tip.visible = false
  tip.renderOrder = 4

  const group = new THREE.Group()
  group.name = 'icon'
  group.scale.set(5 * xishu, 5 * xishu, 5 * xishu)
  const iconPosition = scatterConfig.iconPosition || [0, 0, 0]
  group.position.set(x + iconPosition[0], -y + iconPosition[1], depth + 2 * xishu + iconPosition[2])
  group.renderOrder = 3
  group.add(pin, tip, pinSelected)

  group.scale.set(0, 0, 0)
  const delay = intro ? 2 + Math.random() : 0
  const iconScale = scatterConfig.iconScale || 1
  gsap.to(group.scale, {
    duration: 1,
    x: 5 * xishu * iconScale,
    y: 5 * xishu * iconScale,
    z: 5 * xishu * iconScale,
    ease: 'power2.inOut',
    delay: delay
  })

  return group
}

export const renderScatters = (
  mapId,
  scatterData,
  scatterConfig,
  mapConfig,
  handleScatterHighlight
) => {
  window.clearInterval(timer[mapId])
  currentScatterHighlight = null
  // 从 map 中移除所有 scatter
  const scatters = _map[mapId].children.filter((item) => item.name === 'icon')
  for (let i = 0; i < scatters.length; i++) {
    const tip = scatters[i].children.find((item) => item.name.includes('icon-tip-'))
    scatters[i].remove(tip)
    _map[mapId].remove(scatters[i])
  }
  scatterData?.forEach((item) => {
    const icon = createScatter(
      item,
      mapConfig.depth * _xishu[mapId],
      _offsetXY[mapId],
      scatterConfig,
      mapConfig.intro.show,
      _xishu[mapId]
    )
    _map[mapId].add(icon)
  })
  let pause = false // 是否暂停
  let idx = 0
  const iconScale = scatterConfig.iconScale || 1
  const iconScaleSelected = scatterConfig.iconScaleSelected || 2
  const icons = _map[mapId].children.filter((item) => item.name === 'icon')
  const setIconHighLight = (icon) => {
    removeHighLight()
    gsap.to(icon.scale, {
      duration: 0.25,
      x: 5 * _xishu[mapId] * iconScale * iconScaleSelected,
      y: 5 * _xishu[mapId] * iconScale * iconScaleSelected,
      z: 5 * _xishu[mapId] * iconScale * iconScaleSelected
    })
    icon.children.forEach((child) => {
      if (child.name.includes('icon-tip-')) child.visible = true
      if (child.name === 'pin-selected') child.visible = true
      if (child.name === 'pin') child.visible = false
    })
    if (icon.children[0]?.iconData && icon.children[0].iconData.name !== currentScatterHighlight) {
      currentScatterHighlight = icon.children[0].iconData.name
      handleScatterHighlight(icon.children[0].iconData)
    }
  }
  const removeHighLight = () => {
    icons.forEach((item) => {
      gsap.to(item.scale, {
        duration: 0.1,
        x: 5 * _xishu[mapId] * iconScale,
        y: 5 * _xishu[mapId] * iconScale,
        z: 5 * _xishu[mapId] * iconScale
      })
      // item.scale.set(0.25, 0.25, 0.25)
      item.children.forEach((child) => {
        if (child.name.includes('icon-tip-')) child.visible = false
        if (child.name === 'pin-selected') child.visible = false
        if (child.name === 'pin') child.visible = true
      })
    })
  }
  removeHighLight()
  timer[mapId] = window.setInterval(() => {
    if (pause) return
    setIconHighLight(icons[idx])
    idx++
    if (idx === icons.length) idx = 0
  }, 5000)
  const handler = (event) => {
    const mouse = new THREE.Vector2()
    //将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    if (mapConfig.fullScreen) {
      mouse.x =
        ((event.clientX - window.innerWidth * (mapConfig.offsetX || 0)) / window.innerWidth) * 2 - 1
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
    } else {
      const container = document.getElementById(mapId) as HTMLElement
      const containerRect = container.getBoundingClientRect()
      mouse.x = ((event.clientX - containerRect.left) / container.clientWidth) * 2 - 1
      mouse.y = -((event.clientY - containerRect.top) / container.clientHeight) * 2 + 1
    }
    const raycaster = new THREE.Raycaster()
    // 通过摄像机和鼠标位置更新射线
    // 计算物体和射线的焦点
    raycaster.setFromCamera(mouse, _camera[mapId])
    const intersects = raycaster
      .intersectObjects(_map[mapId].children)
      .filter((item) => item.object.parent.name === 'icon')
    if (intersects.length > 0) {
      pause = true
      const icon = intersects[0].object.parent
      setIconHighLight(icon)
    } else {
      pause = false
      // removeHighLight()
    }
  }
  if (onMouseMove1[mapId]) window.removeEventListener('mousemove', onMouseMove1[mapId])
  onMouseMove1[mapId] = debounce(handler, 100)
  window.addEventListener('mousemove', onMouseMove1[mapId])
}

export const renderSlots = (mapId, slotData, slotConfig, mapConfig, geoData) => {
  // 从 map 中移除所有 slot
  const slots = _map[mapId].children.filter((item) => item.name === 'slot')
  for (let i = 0; i < slots.length; i++) {
    const node = slots[i].children.find((item) => item.name.includes('slot-node-'))
    const tooltip = slots[i].children.find((item) => item.name.includes('slot-tooltip-'))
    slots[i].remove(node)
    slots[i].remove(tooltip)
    _map[mapId].remove(slots[i])
  }
  if (!slotData) return
  if (slotConfig.type === 'scatter') {
    slotData.forEach((item) => {
      const slot = createSlot(
        item,
        mapConfig.depth * _xishu[mapId],
        _offsetXY[mapId],
        slotConfig,
        mapConfig.intro.show,
        _xishu[mapId]
      )
      _map[mapId].add(slot)
    })
  } else {
    geoData.features.forEach((feature) => {
      const { centroid, center, name } = feature.properties
      const position = centroid || center || [0, 0]
      const item = slotData.find((item) => item.name === name)
      if (item) {
        const slot = createSlot(
          {
            ...item,
            position
          },
          mapConfig.depth * _xishu[mapId],
          _offsetXY[mapId],
          slotConfig,
          mapConfig.intro.show,
          _xishu[mapId]
        )
        _map[mapId].add(slot)
      }
    })
  }
  let pause = false // 是否暂停
  let idx = 0
  const nodeScale = slotConfig.nodeScale || 1
  const nodeScaleSelected = slotConfig.nodeScaleSelected || 2
  const nodes = _map[mapId].children.filter((item) => item.name === 'slot')
  const setNodeHighLight = (node) => {
    removeHighLight()
    // gsap.to(node.scale, {
    //   duration: 0.25,
    //   x: 5 * _xishu[mapId] * nodeScale * nodeScaleSelected,
    //   y: 5 * _xishu[mapId] * nodeScale * nodeScaleSelected,
    //   z: 5 * _xishu[mapId] * nodeScale * nodeScaleSelected
    // })
    node.children.forEach((child) => {
      if (child.name.includes('slot-tooltip-')) child.visible = true
    })
  }
  const removeHighLight = () => {
    nodes.forEach((item) => {
      // gsap.to(item.scale, {
      //   duration: 0.1,
      //   x: 5 * _xishu[mapId] * nodeScale,
      //   y: 5 * _xishu[mapId] * nodeScale,
      //   z: 5 * _xishu[mapId] * nodeScale
      // })
      // item.scale.set(0.25, 0.25, 0.25)
      item.children.forEach((child) => {
        if (child.name.includes('slot-tooltip-')) child.visible = false
      })
    })
  }
  removeHighLight()
  timer[mapId] = window.setInterval(() => {
    if (pause) return
    // setNodeHighLight(nodes[idx])
    idx++
    if (idx === nodes.length) idx = 0
  }, 5000)
  const handler = (event) => {
    const mouse = new THREE.Vector2()
    //将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    if (mapConfig.fullScreen) {
      mouse.x =
        ((event.clientX - window.innerWidth * (mapConfig.offsetX || 0)) / window.innerWidth) * 2 - 1
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
    } else {
      const container = document.getElementById(mapId) as HTMLElement
      const containerRect = container.getBoundingClientRect()
      mouse.x = ((event.clientX - containerRect.left) / container.clientWidth) * 2 - 1
      mouse.y = -((event.clientY - containerRect.top) / container.clientHeight) * 2 + 1
    }
    const raycaster = new THREE.Raycaster()
    // 通过摄像机和鼠标位置更新射线
    // 计算物体和射线的焦点
    raycaster.setFromCamera(mouse, _camera[mapId])
    const intersects = raycaster
      .intersectObjects(_map[mapId].children)
      .filter((item) => item.object.parent.name === 'slot')
    if (intersects.length > 0) {
      pause = true
      const icon = intersects[0].object.parent
      setNodeHighLight(icon)
    } else {
      pause = false
      // removeHighLight()
    }
  }
  if (onMouseMove4[mapId]) window.removeEventListener('mousemove', onMouseMove4[mapId])
  onMouseMove4[mapId] = debounce(handler, 100)
  window.addEventListener('mousemove', onMouseMove4[mapId])
}

const createSlot = (slotDataItem, depth, offsetXY, slotConfig, intro, xishu) => {
  const data = slotDataItem
  const nodeDOM = document.createElement('div')
  Object.entries(slotConfig.nodeStyle).forEach(([key, value]) => {
    nodeDOM.style[key] = value
  })
  nodeDOM.innerHTML = data.node
  const node = new CSS2DObject(nodeDOM)
  node.name = 'slot-node-' + data.node
  node._data = data
  const [x, y] = offsetXY(data.position)

  // 创建一个球体
  const geometry = new THREE.SphereGeometry(0.2, 32, 32)
  const material = new THREE.MeshBasicMaterial({ color: 0xffff00, transparent: true, opacity: 0 })
  const sphere = new THREE.Mesh(geometry, material)
  sphere.name = 'sphere'

  const group = new THREE.Group()
  group.name = 'slot'
  group.position.set(x, -y, depth + 2 * xishu)
  group.add(node, sphere)
  group.renderOrder = 9

  if (data.tooltip) {
    const tooltipDOM = document.createElement('div')
    Object.entries(slotConfig.labelStyle).forEach(([key, value]) => {
      tooltipDOM.style[key] = value
    })
    tooltipDOM.innerHTML = data.tooltip
    const tooltip = new CSS2DObject(tooltipDOM)
    const tipPosition = slotConfig.labelPosition || { x: 0, y: 0 }
    tooltip.position.set(tipPosition.x, tipPosition.y, 0)
    tooltip.visible = false
    tooltip.name = 'slot-tooltip-' + data.tooltip
    group.add(tooltip)
  }

  return group
}

// 创建柱状图组
const createBarGroup = (mapId, point, depth, name, offsetXY, barData, barConfig, xishu) => {
  const barItem = barData.find((item) => item.name === name)
  const max = Math.max(
    ...barData.map((item) => item.value),
    ...barData.map((item) => item.value2 || 0)
  )
  if (!barItem) {
    // console.log('柱状图数据有误')
    return new THREE.Group()
  }
  if (max === 0) {
    // console.log('无有效数据')
    return new THREE.Group()
  }
  const group = new THREE.Group()
  group.name = 'bar-' + name
  // 创建圆柱体的几何体
  const height = (barConfig.height / max) * barItem.value * xishu // 计算高度
  const geometry =
    barConfig.shape === 'cube'
      ? new THREE.BoxGeometry(
          barConfig.radius * xishu * 2,
          height,
          barConfig.radius * xishu * 2,
          32
        )
      : new THREE.CylinderGeometry(barConfig.radius * xishu, barConfig.radius * xishu, height, 32)
  // 创建材质
  const material = new THREE.MeshStandardMaterial(barConfig.material)
  // 创建圆柱体网格
  const cylinder = new THREE.Mesh(geometry, material)
  cylinder.name = 'cylinder1-' + name
  group.add(cylinder)

  let groupHeight = height

  // 第二根柱子
  if (barItem.value2) {
    const height2 = (barConfig.height / max) * barItem.value2 * xishu // 计算高度
    const geometry2 =
      barConfig.shape === 'cube'
        ? new THREE.BoxGeometry(
            barConfig.radius * xishu * 2,
            height2,
            barConfig.radius * xishu * 2,
            32
          )
        : new THREE.CylinderGeometry(
            barConfig.radius * xishu,
            barConfig.radius * xishu,
            height2,
            32
          )
    const material2 = new THREE.MeshStandardMaterial(barConfig.material2)
    const cylinder2 = new THREE.Mesh(geometry2, material2)
    cylinder2.name = 'cylinder2-' + name
    cylinder2.position.x = barConfig.radius * xishu * 2 + 0.01
    if (height2 > height) {
      groupHeight = height2
      cylinder.position.y -= (height2 - height) / 2
    } else {
      cylinder2.position.y -= (height - height2) / 2
    }
    group.add(cylinder2)
  }

  // label 文字
  const div = document.createElement('div')
  Object.entries(barConfig.labelStyle).forEach(([key, value]) => {
    div.style[key] = value
  })
  if (barConfig.labelFormatter) {
    div.innerHTML = barConfig.labelFormatter(barItem)
  } else {
    div.innerHTML = [
      `<div style="font-weight: bold;margin-bottom: 8px;">${name}</div>`,
      ...(barItem.info || []).map(
        (item) => `
        <div style="display:flex">
          <span style="color:#ccc">${item.title}：</span>
          <span style="flex: 1">${item.content}</span>
        </div>`
      )
    ].join('')
  }
  const tip = new CSS2DObject(div)
  tip.name = 'tip-' + name
  if (barConfig.labelPosition) {
    tip.position.set(
      barConfig.labelPosition.x,
      groupHeight / 2 + barConfig.labelPosition.y + 10 * _xishu[mapId],
      0
    )
  } else {
    tip.position.set(0, groupHeight / 2 + 10 * _xishu[mapId], 0)
  }
  tip.visible = false
  tip.renderOrder = 5
  // 给光柱添加数值
  group.add(tip)

  // 底部光晕
  const circlePlane = new THREE.PlaneGeometry(0.5, 0.5)
  const circlePlaneUrl = new URL('./assets/img/circlePlane.png', import.meta.url).href
  const circlePlaneTexture = new THREE.TextureLoader().load(circlePlaneUrl)
  const circlePlaneMaterial = new THREE.MeshBasicMaterial({
    color: '#fff',
    map: circlePlaneTexture,
    transparent: true,
    blending: THREE.AdditiveBlending,
    depthWrite: false,
    side: THREE.DoubleSide
  })
  const circlePlaneMesh = new THREE.Mesh(circlePlane, circlePlaneMaterial)
  circlePlaneMesh.rotation.x = -Math.PI / 2
  circlePlaneMesh.position.set(0, -groupHeight / 2 + 0.02, 0)
  circlePlaneMesh.name = 'circlePlane-' + name
  circlePlaneMesh.visible = false
  group.add(circlePlaneMesh)

  gsap.to(circlePlaneMesh.scale, {
    duration: 1,
    x: 1.5,
    y: 1.5,
    z: 1.5,
    repeat: -1,
    yoyo: true,
    ease: 'power2.inOut'
  })

  const [x, y] = offsetXY(point)
  group.rotation.x = Math.PI / 2
  group.position.set(x, -y, depth + groupHeight / 2)
  group.scale.set(0, 0, 0)

  gsap.to(group.scale, {
    x: 1,
    y: 1,
    z: 1,
    duration: 1,
    ease: 'power2.inOut',
    delay: (barConfig.delay >= 0 ? barConfig.delay : 0) + Math.random(),
    onUpdate() {
      group.position.z = depth + (group.scale.y * groupHeight) / 2 + 0.1
    }
  })

  return group
}

export const renderBars = (mapId, barData, barConfig, geoData, mapConfig, handleBarHighlight) => {
  window.clearInterval(timer[mapId])
  currentBarHighlight = null
  // 从 map 中移除所有 bar
  const bars = _map[mapId].children.filter((item) => item.name.includes('bar-'))
  for (let i = 0; i < bars.length; i++) {
    const tip = bars[i].children.find((item) => item.name.includes('tip-'))
    bars[i].remove(tip)
    _map[mapId].remove(bars[i])
  }
  if (!barData) return
  if (barConfig.type === 'scatter') {
    barData.forEach((item) => {
      const barGroup = createBarGroup(
        mapId,
        item.position,
        mapConfig.depth * _xishu[mapId],
        item.name,
        _offsetXY[mapId],
        barData,
        barConfig,
        _xishu[mapId]
      ) //创建柱状图组
      _map[mapId].add(barGroup)
    })
  } else {
    geoData.features.forEach((feature) => {
      const { centroid, center, name } = feature.properties
      const point = centroid || center || [0, 0]
      const barGroup = createBarGroup(
        mapId,
        point,
        mapConfig.depth * _xishu[mapId],
        name,
        _offsetXY[mapId],
        barData,
        barConfig,
        _xishu[mapId]
      ) //创建柱状图组
      _map[mapId].add(barGroup)
    })
  }
  let pause = false // 是否暂停
  let idx = 0
  const barGroup = _map[mapId].children.filter((item) => item.name.includes('bar-'))
  const mapGroup = _map[mapId].children.filter((item) => item._type === 'block')
  const labelGroup = _map[mapId].children.filter((item) => item.name.includes('label-'))

  const material = new THREE.MeshStandardMaterial(barConfig.material)
  const material2 = new THREE.MeshStandardMaterial(barConfig.material2)
  const materialHighlight = new THREE.MeshStandardMaterial(barConfig.materialHighlight)
  const material2Highlight = new THREE.MeshStandardMaterial(barConfig.material2Highlight)

  const removeHighLight = () => {
    barGroup.forEach((item) => {
      item.children.forEach((child) => {
        if (child.name.includes('tip-')) child.visible = false
        if (child.name.includes('circlePlane-')) child.visible = false
        if (child.name.includes('cylinder1-')) child.material = material
        if (child.name.includes('cylinder2-')) child.material = material2
      })
    })
    // mapGroup.forEach((item) => {
    //   item.children.forEach((child) => {
    //     if (child.name === 'map') {
    //       child.material[0].color.set(mapConfig.material.color)
    //     }
    //   })
    // })
    labelGroup.forEach((item) => {
      Object.entries(mapConfig.labelStyle).forEach(([key, value]) => {
        item.element.children[0].style[key] = value
      })
    })
  }

  removeHighLight()

  const setHighLight = (idx) => {
    removeHighLight()
    // 执行高亮
    barGroup[idx]?.children.forEach((item) => {
      if (item.name.includes('cylinder1-')) item.material = materialHighlight
      if (item.name.includes('cylinder2-')) item.material = material2Highlight
      if (item.name.includes('tip-')) item.visible = true
      if (barConfig.type === 'scatter') return
      if (item.name.includes('circlePlane-')) item.visible = true
    })
    if (barConfig.type === 'scatter') return
    const name = barGroup[idx]?.name.split('-')[1]
    if (!name) return
    const mapIndex = mapGroup.findIndex((item) => item.name === name)
    // if (mapIndex === -1) return
    // mapGroup[mapIndex].children.forEach((item) => {
    //   if (item.name === 'map') {
    //     item.material[0].color.set(mapConfig.materialHighlight.color)
    //   }
    // })
    Object.entries(mapConfig.labelStyleHighlight).forEach(([key, value]) => {
      labelGroup[mapIndex].element.children[0].style[key] = value
    })
    if (barData[idx]?.name && barData[idx].name !== currentBarHighlight) {
      currentBarHighlight = barData[idx].name
      handleBarHighlight(barData[idx])
    }
  }
  timer[mapId] = window.setInterval(() => {
    if (pause) return
    setHighLight(idx)
    idx++
    if (idx === barGroup.length) idx = 0
  }, 5000)
  // 鼠标事件
  if (onMouseMove2[mapId]) window.removeEventListener('mousemove', onMouseMove2[mapId])
  onMouseMove2[mapId] = (event) => {
    const mouse = new THREE.Vector2()
    //将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    if (mapConfig.fullScreen) {
      mouse.x =
        ((event.clientX - window.innerWidth * (mapConfig.offsetX || 0)) / window.innerWidth) * 2 - 1
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
    } else {
      const container = document.getElementById(mapId) as HTMLElement
      const containerRect = container.getBoundingClientRect()
      mouse.x = ((event.clientX - containerRect.left) / container.clientWidth) * 2 - 1
      mouse.y = -((event.clientY - containerRect.top) / container.clientHeight) * 2 + 1
    }
    const raycaster = new THREE.Raycaster()
    // 通过摄像机和鼠标位置更新射线
    // 计算物体和射线的焦点
    raycaster.setFromCamera(mouse, _camera[mapId])
    const intersects = raycaster
      .intersectObjects(_map[mapId].children)
      .filter((item) => item.object.type !== 'Line')
    if (intersects.length > 0) {
      if (intersects[0].object.parent.name !== '' && intersects[0].object.name === 'map') {
        pause = true
        const name = intersects[0].object.parent.name
        const groupIndex = barGroup.findIndex((item) => item.name === 'bar-' + name)
        setHighLight(groupIndex)
      } else if (intersects[0].object.parent.name.includes('bar-')) {
        pause = true
        const name = intersects[0].object.parent.name.split('-')[1]
        const groupIndex = barGroup.findIndex((item) => item.name === 'bar-' + name)
        setHighLight(groupIndex)
      } else if (intersects[0].object.name === 'plane') {
        pause = false
      }
    } else {
      pause = false
    }
  }
  window.addEventListener('mousemove', onMouseMove2[mapId])
}

// 创建两点间飞线
const createFlyLine = (data, offsetXY, mapConfig, flyLineConfig, xishu) => {
  const [x0, y0, z0] = [...offsetXY(data.start), mapConfig.depth * xishu]
  const [x1, y1, z1] = [...offsetXY(data.end), mapConfig.depth * xishu]
  // 使用 QuadraticBezierCurve3 创建 三维二次贝塞尔曲线
  const curve = new THREE.QuadraticBezierCurve3(
    new THREE.Vector3(x0, -y0, z0),
    new THREE.Vector3((x0 + x1) / 2, -(y0 + y1) / 2, flyLineConfig.height),
    new THREE.Vector3(x1, -y1, z1)
  )

  const lineGeometry = new THREE.BufferGeometry()
  // 获取曲线上50个点
  const points = curve.getPoints(50)
  const positions: number[] = []
  for (let j = 0; j < points.length; j++) {
    positions.push(points[j].x, points[j].y, points[j].z)
  }
  // 放入顶点
  lineGeometry.setAttribute(
    'position',
    new THREE.BufferAttribute(new Float32Array(positions), 3, true)
  )

  const material = new THREE.LineBasicMaterial({
    ...flyLineConfig.material,
    color: data.color || flyLineConfig.material.color
  })
  // const flyLine = new THREE.Line(lineGeometry, material)

  const path = new THREE.CatmullRomCurve3(points)
  const radius = data.current ? 2.5 * (flyLineConfig.radius || 0.2) : flyLineConfig.radius || 0.2
  const tubeGeometry = new THREE.TubeGeometry(path, 500, radius * xishu, 8, false)
  const flyLineTube = new THREE.Mesh(tubeGeometry, material)
  flyLineTube.name = 'flyLine-' + data.start + '-' + data.end
  flyLineTube._type = 'flyLine'

  return flyLineTube
}

export const renderFlyLines = (mapId, list, mapConfig, flyLineConfig, geoData) => {
  // 从 map 中移除所有 flyline
  const lines = _map[mapId].children.filter((item) => item._type === 'flyLine')
  for (let i = 0; i < lines.length; i++) {
    _map[mapId].remove(lines[i])
  }
  list?.forEach((item) => {
    if (flyLineConfig.type === 'block' && item.startName && item.endName) {
      const featureStart = geoData.features.find(
        (feature) => feature.properties.name === item.startName
      )
      const featureEnd = geoData.features.find(
        (feature) => feature.properties.name === item.endName
      )
      if (featureStart && featureEnd) {
        item.start = featureStart.properties.centroid || featureStart.properties.center || [0, 0]
        item.end = featureEnd.properties.centroid || featureEnd.properties.center || [0, 0]
      }
    }
    const line = createFlyLine(item, _offsetXY[mapId], mapConfig, flyLineConfig, _xishu[mapId])
    _map[mapId].add(line)
  })
}

export const renderHighlightBlocks = (mapId, list, mapConfig) => {
  if (!list) return
  const blocks = _map[mapId].children.filter((item) => item._type === 'block')
  const maps = blocks.map((item) => item.children.find((child) => child.name === 'map'))
  // 修改 maps 颜色
  maps.forEach((item) => {
    if (list.find((h) => h.name === item.parent.name)) {
      const color = list.find((h) => h.name === item.parent.name).color
      item.material[0].color.set(color)
    } else {
      item.material[0].color.set(mapConfig.material.color)
    }
  })
}

// 生成底部波纹效果
const createRipples = (mapId, config) => {
  const group = new THREE.Group()
  group.name = 'plane'

  let radius = 20 * _xishu[mapId]
  for (let i = 0; i < 5; i++) {
    const circle = new THREE.ArcCurve(0, 0, radius, 0, Math.PI * 2, false)
    const points = circle.getPoints(100)
    const circleGeometry = new THREE.BufferGeometry().setFromPoints(points)
    const { size, material } = config

    const circleMaterial = new THREE.LineDashedMaterial({
      transparent: true,
      side: THREE.DoubleSide,
      opacity: material.opacity || 0.5,
      color: material.color || '#fff',
      dashSize: (material.dashSize || 0.25) * _xishu[mapId],
      gapSize: (material.gapSize || 1) * _xishu[mapId]
    })
    const circleMesh = new THREE.Line(circleGeometry, circleMaterial)
    circleMesh.computeLineDistances()
    circleMesh.position.z = 0.01
    group.add(circleMesh)
    radius += (size || 10) * _xishu[mapId]
  }

  gsap.to(group.scale, {
    duration: 5,
    x: 1.5,
    y: 1.5,
    z: 1.5,
    // yoyo: true,
    repeat: -1
  })
  return group
}

// 获取地图中心点
const getMapCenterAndSize = (geoData) => {
  const points = geoData.features.map((feature) => {
    const { centroid, center } = feature.properties
    const point = centroid || center || [0, 0]
    return new THREE.Vector2(point[0], point[1])
  })
  const box = new THREE.Box2()
  points.forEach((point) => box.expandByPoint(point))
  const center = box.getCenter(new THREE.Vector2())
  const size = box.getSize(new THREE.Vector2())
  if (size.x === 0 && size.y === 0 && geoData.features[0].geometry.coordinates[0][0]) {
    const box2 = new THREE.Box2()
    geoData.features[0].geometry.coordinates[0][0]
      .map((item) => new THREE.Vector2(item[0], item[1]))
      .forEach((point) => box2.expandByPoint(point))
    const size2 = box2.getSize(new THREE.Vector2())
    size.x = size2.x
    size.y = size2.y
  }
  return {
    center: [center.x, center.y],
    size: Math.max(size.x, size.y) || 3
  }
}

// 创建地图
export const createMap = (params) => {
  const {
    mapId,
    geoData,
    barData,
    mapConfig,
    barConfig,
    scatterData,
    scatterConfig,
    blockData,
    blockConfig,
    flyLineData,
    flyLineConfig,
    bgConfig
  } = params
  const map = new THREE.Object3D()
  const centerAndSize = getMapCenterAndSize(geoData)
  const mapCenter = centerAndSize.center
  const xishu = centerAndSize.size / 42.4
  _xishu[mapId] = xishu
  // 将经纬度解析为空间坐标
  const offsetXY = d3.geoMercator()
  offsetXY.center(mapCenter).translate([0, 0])
  _offsetXY[mapId] = offsetXY
  let ripplesFlag = false // 解决重复绘制波纹的问题
  geoData.features.forEach((feature) => {
    const unit = new THREE.Object3D()
    const { centroid, center, name } = feature.properties
    const { coordinates, type } = feature.geometry
    const point = centroid || center || [0, 0]
    const depth = mapConfig.depth //定义地图高度

    const label = createLabel(name, point, mapConfig, offsetXY, blockData, blockConfig, xishu)

    coordinates.forEach((coordinate) => {
      if (type === 'MultiPolygon') coordinate.forEach((item) => fn(item))
      if (type === 'Polygon') fn(coordinate)

      function fn(coordinate) {
        unit.name = name
        unit._type = 'block'
        unit._properties = feature.properties
        const mesh = createMesh(
          coordinate,
          mapConfig,
          offsetXY,
          blockData,
          blockConfig,
          name,
          point,
          xishu
        )
        const line = createLine(coordinate, mapConfig, offsetXY, xishu)
        unit.add(mesh, ...line)
      }
    })
    map.add(unit, label)
    if (bgConfig.ripples?.show && !ripplesFlag) {
      ripplesFlag = true
      const ripples = createRipples(mapId, bgConfig.ripples)
      map.add(ripples)
    }
  })
  setCenter(map)
  _map[mapId] = map
  console.log('map', map)

  return map
}

// 创建背景地图
export const createBgMap = (params) => {
  const { bgData, bgConfig, mapCenter } = params

  const createBgMesh = (data, offsetXY) => {
    const shape = new THREE.Shape()
    data.forEach((item, idx) => {
      const [x, y] = offsetXY(item)

      if (idx === 0) shape.moveTo(x, -y)
      else shape.lineTo(x, -y)
    })

    const geometry = new THREE.ExtrudeGeometry(shape, {
      depth: 0.01,
      bevelEnabled: false
    })
    const materialParams = bgConfig.material || {
      color: '#fff',
      side: THREE.DoubleSide,
      transparent: true,
      opacity: 0
    }
    const material = new THREE.MeshBasicMaterial(materialParams)
    const mesh = new THREE.Mesh(geometry, material)
    mesh.position.set(0, 0, 0)
    return mesh
  }

  const createBgLine = (data, offsetXY) => {
    const points = [] as any
    data.forEach((item) => {
      const [x, y] = offsetXY(item)
      points.push(new THREE.Vector3(x, -y, 0))
    })
    const lineGeometry = new THREE.BufferGeometry().setFromPoints(points)
    const lineMaterial = new THREE.LineBasicMaterial(bgConfig.lineMaterial)
    const lineMesh = new THREE.Line(lineGeometry, lineMaterial)
    lineMesh.position.z = 0.01
    return lineMesh
  }

  const bgGeoData = bgData
  const bgMap = new THREE.Object3D()
  const offsetXY = d3.geoMercator()
  offsetXY.center(mapCenter).translate([0, 0])
  bgGeoData.features.forEach((feature) => {
    const unit = new THREE.Object3D()
    // const { name } = feature.properties
    const { coordinates, type } = feature.geometry
    coordinates.forEach((coordinate) => {
      if (type === 'MultiPolygon') coordinate.forEach((item) => fn(item))
      if (type === 'Polygon') fn(coordinate)

      function fn(coordinate) {
        // unit.name = name
        const mesh = createBgMesh(coordinate, offsetXY)
        const line = createBgLine(coordinate, offsetXY)
        unit.add(mesh, line)
      }
    })
    bgMap.add(unit)
  })
  setCenter(bgMap)
  return bgMap
}

// 创建控制器
export const createControls = (camera, renderer) => {
  const controls = new OrbitControls(camera, renderer.domElement)
  // 设置控制器阻尼
  controls.enableDamping = true
  controls.update()
  //上下翻转的最大角度
  controls.maxPolarAngle = 1.2
  //上下翻转的最小角度
  controls.minPolarAngle = 0
  // 禁止相机平移
  controls.enablePan = false
  return controls
}

// 创建 labelRender
export const createLabelRenderer = (mapId, mapConfig) => {
  const container = document.getElementById(mapId) as HTMLElement
  const WIDTH = mapConfig.fullScreen ? mapConfig.screenWidth : container.clientWidth
  const HEIGHT = mapConfig.fullScreen ? mapConfig.screenHeight : container.clientHeight
  const labelRenderer = new CSS2DRenderer()
  labelRenderer.domElement.style.position = 'absolute'
  labelRenderer.domElement.style.top = '0px'
  labelRenderer.domElement.style.pointerEvents = 'none'
  labelRenderer.setSize(WIDTH, HEIGHT)
  return labelRenderer
}

// 创建场景
export const createScene = (mapConfig) => {
  const scene = new THREE.Scene()

  if (mapConfig.axesHelper && mapConfig.axesHelper.show) {
    const axesHelper = new THREE.AxesHelper(1000)
    scene.add(axesHelper)
  }

  return scene
}

// 创建相机
export const createCamera = (mapId, mapConfig) => {
  const container = document.getElementById(mapId) as HTMLElement
  const WIDTH = mapConfig.fullScreen ? mapConfig.screenWidth : container.clientWidth
  const HEIGHT = mapConfig.fullScreen ? mapConfig.screenHeight : container.clientHeight
  const camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 1, 1000)
  camera.position.set(0, 500, 0)
  camera.lookAt(0, 0, 0)

  // 设置进入的动画
  const cameraPosition = mapConfig.cameraPosition || [0, 200, 200]
  if (mapConfig.intro.show) {
    gsap.to(camera.position, {
      duration: mapConfig.intro.duration,
      x: cameraPosition[0],
      y: cameraPosition[1],
      z: cameraPosition[2],
      ease: mapConfig.intro.ease
    })
  } else {
    camera.position.set(...cameraPosition)
  }

  return camera
}

// 创建渲染器
export const createRenderer = (mapId, mapConfig) => {
  const container = document.getElementById(mapId) as HTMLElement
  const WIDTH = mapConfig.fullScreen ? mapConfig.screenWidth : container.clientWidth
  const HEIGHT = mapConfig.fullScreen ? mapConfig.screenHeight : container.clientHeight
  const renderer = new THREE.WebGLRenderer({
    alpha: true, // 设置抗锯齿
    antialias: true,
    // 设置物理灯光模拟效果
    physicallyCorrectLights: true,
    // 设置对数深度缓冲区
    logarithmicDepthBuffer: true
  })
  // 设置渲染尺寸大小
  renderer.setSize(WIDTH, HEIGHT)
  return renderer
}

// 初始化光源
export const initLights = (scene, mapEnv) => {
  const color = mapEnv.envLightColor || '#fff'
  const intensity = mapEnv.envLightIntensity || 4
  const ambientLight = new THREE.AmbientLight(color, intensity)
  scene.add(ambientLight)
  const directionalLight = new THREE.DirectionalLight('#fff', 0.2)
  directionalLight.position.set(0, 30, 50)
  const directionalLight2 = directionalLight.clone()
  directionalLight2.position.set(0, 30, -50)
  const directionalLight3 = directionalLight.clone()
  directionalLight3.position.set(-50, 30, 0)
  const directionalLight4 = directionalLight.clone()
  directionalLight4.position.set(50, 30, 0)
  const directionalLight5 = directionalLight.clone()
  directionalLight5.position.set(35, 60, 50)
  const directionalLight6 = directionalLight.clone()
  directionalLight6.position.set(-35, 60, 50)
  const directionalLight7 = directionalLight.clone()
  directionalLight7.position.set(-35, 60, -50)
  const directionalLight8 = directionalLight.clone()
  directionalLight8.position.set(25, 50, -50)
  const directionalLight9 = directionalLight.clone()
  directionalLight9.position.set(0, 60, -60)
  const directionalLight10 = directionalLight.clone()
  directionalLight10.position.set(0, 60, 40)
  scene.add(directionalLight)
  scene.add(directionalLight2)
  scene.add(directionalLight3)
  scene.add(directionalLight4)
  scene.add(directionalLight5)
  scene.add(directionalLight6)
  scene.add(directionalLight7)
  scene.add(directionalLight8)
  scene.add(directionalLight9)
  scene.add(directionalLight10)

  const lightX = new THREE.DirectionalLight(color, intensity / 2)
  const lightX2 = new THREE.DirectionalLight(color, intensity / 2)
  lightX.position.set(10, 0, 0)
  lightX2.position.set(-10, 0, 0)
  scene.add(lightX)
}

// 渲染地图和数据
export const renderMap = ({
  mapId,
  scene,
  camera,
  geoData,
  barData,
  mapConfig,
  barConfig,
  scatterData,
  scatterConfig,
  blockData,
  blockConfig,
  flyLineData,
  flyLineConfig,
  bgData,
  bgConfig,
  handleBlockClick
}) => {
  _camera[mapId] = camera
  //获取地图边界
  const map = createMap({
    mapId,
    geoData,
    barData,
    mapConfig,
    barConfig,
    scatterData,
    scatterConfig,
    blockData,
    blockConfig,
    flyLineData,
    flyLineConfig,
    bgConfig
  })
  const { size, center } = getMapCenterAndSize(geoData)
  const xishu = size / 42.4
  const mapScale = (50 / size) * (mapConfig.mapScale || 1)
  const mapPosition = mapConfig.mapPosition || [0, 0, 0]
  map.scale.set(mapScale, mapScale, mapScale)
  map.position.set(...mapPosition)
  scene.add(map)

  // 获取背景地图
  if (bgData) {
    const bgMap = createBgMap({ bgData, bgConfig: bgConfig.map, mapCenter: center })
    bgMap.scale.set(mapScale, mapScale, mapScale)
    bgMap.position.set(...mapPosition)
    scene.add(bgMap)
  }
  if (blockData) {
    let pause = false // 是否暂停
    let idx = 0
    const blockTips = [] as any
    map.traverse((item) => {
      if (item.name.includes('block-tip-')) {
        blockTips.push(item)
      }
    })
    const labelGroup = map.children.filter((item) => item.name.includes('label-'))

    const showTip = (name) => {
      hideAllTips()
      blockTips.forEach((item) => {
        if (item.name.includes(name)) {
          // item.visible = true
          const label = labelGroup.find((label) => label.name.includes(name))
          Object.entries(mapConfig.labelStyleHighlight).forEach(([key, value]) => {
            label.element.children[0].style[key] = value
          })
          if (label.element.children[1]) {
            label.element.children[1].style.visibility = 'visible'
          }
        }
      })
    }
    const hideAllTips = () => {
      blockTips.forEach((item) => {
        item.visible = false
      })
      labelGroup.forEach((item) => {
        Object.entries(mapConfig.labelStyle).forEach(([key, value]) => {
          item.element.children[0].style[key] = value
        })
        if (item.element.children[1]) {
          item.element.children[1].style.visibility = 'hidden'
        }
      })
    }

    timer[mapId] = window.setInterval(() => {
      if (blockConfig.carousel === false) return
      if (pause) return
      const name = blockTips[idx].name.split('-')[2]
      showTip(name)
      idx++
      if (idx === blockTips.length) idx = 0
    }, 5000)

    // 鼠标事件
    if (onMouseMove3[mapId]) window.removeEventListener('mousemove', onMouseMove3[mapId])
    onMouseMove3[mapId] = (event) => {
      const mouse = new THREE.Vector2()
      //将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
      if (mapConfig.fullScreen) {
        mouse.x =
          ((event.clientX - window.innerWidth * (mapConfig.offsetX || 0)) / window.innerWidth) * 2 -
          1
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
      } else {
        const container = document.getElementById(mapId) as HTMLElement
        const containerRect = container.getBoundingClientRect()
        mouse.x = ((event.clientX - containerRect.left) / container.clientWidth) * 2 - 1
        mouse.y = -((event.clientY - containerRect.top) / container.clientHeight) * 2 + 1
      }
      const raycaster = new THREE.Raycaster()
      // 通过摄像机和鼠标位置更新射线
      // 计算物体和射线的焦点
      raycaster.setFromCamera(mouse, camera)
      const intersects = raycaster
        .intersectObjects(map.children)
        .filter((item) => item.object.type !== 'Line')
      if (intersects.length > 0) {
        if (intersects[0].object.parent.name !== '' && intersects[0].object.name === 'map') {
          const name = intersects[0].object.parent.name
          showTip(name)
        } else if (intersects[0].object.name === 'plane') {
          pause = false
          if (blockConfig.carousel === false) {
            hideAllTips()
          }
        }
      } else {
        pause = false
        if (blockConfig.carousel === false) {
          hideAllTips()
        }
      }
    }
    window.addEventListener('mousemove', onMouseMove3[mapId])
  }

  onClick[mapId] = (event) => {
    const now = new Date().getTime()
    if (now - timestamp > 200) return
    const mouse = new THREE.Vector2()
    //将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    if (mapConfig.fullScreen) {
      mouse.x =
        ((event.clientX - window.innerWidth * (mapConfig.offsetX || 0)) / window.innerWidth) * 2 - 1
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
    } else {
      const container = document.getElementById(mapId) as HTMLElement
      const containerRect = container.getBoundingClientRect()
      mouse.x = ((event.clientX - containerRect.left) / container.clientWidth) * 2 - 1
      mouse.y = -((event.clientY - containerRect.top) / container.clientHeight) * 2 + 1
    }
    const raycaster = new THREE.Raycaster()
    // 通过摄像机和鼠标位置更新射线
    // 计算物体和射线的焦点
    raycaster.setFromCamera(mouse, camera)
    const intersects = raycaster
      .intersectObjects(map.children)
      .filter((item) => item.object.type !== 'Line')
    if (intersects.length > 0) {
      if (intersects[0].object.parent.name !== '' && intersects[0].object.name === 'map') {
        handleBlockClick(intersects[0].object.parent)
      }
    }
  }
  window.addEventListener('mouseup', onClick[mapId])

  onMouseDown[mapId] = () => {
    timestamp = new Date().getTime()
  }
  window.addEventListener('mousedown', onMouseDown[mapId])

  return map
}

function debounce(fn: any, time = 50) {
  let tr: number | null = null
  return function (this: any, ...args: any) {
    clearTimeout(tr as number)
    tr = window.setTimeout(() => {
      fn.apply(this, args)
    }, time)
  }
}
